#include <windows.h>
#include <stdio.h>
#include <iostream>

using namespace std; // 添加这一行，以便在全局范围内使用 std 命名空间

const unsigned short SIZE_OF_BUFFER = 10; //缓冲区长度
unsigned short ProductID = 0;    //产品号
unsigned short ConsumeID = 0;    //将被消耗的产品号
unsigned short in = 0;      //产品进缓冲区时的缓冲区下标
unsigned short out = 0;      //产品出缓冲区时的缓冲区下标

int g_buffer[SIZE_OF_BUFFER];    //缓冲区是个循环队列
bool g_continue = true;      //控制程序结束
HANDLE g_hMutex;       //用于线程间的互斥
HANDLE g_hEmptySemaphore;     //当缓冲区满时迫使生产者等待
HANDLE g_hFullSemaphore;     //当缓冲区空时迫使消费者等待

DWORD WINAPI Producer(LPVOID);    //生产者线程
DWORD WINAPI Consumer(LPVOID);    //消费者线程

int main() {
    //新建一个文件，用于记录生产者消费者访问缓冲区的过程
    FILE *fp;
    if((fp=fopen("info.txt","w"))==NULL) {
        cout << "错误,文件打不开,请检查文件名" << endl;
        return 0;
    }
    fclose(fp);

    //创建各互斥对象和信号量对象
    g_hMutex = CreateMutex(NULL, FALSE, NULL);
    g_hEmptySemaphore = CreateSemaphore(NULL, SIZE_OF_BUFFER, SIZE_OF_BUFFER, NULL);
    g_hFullSemaphore = CreateSemaphore(NULL, 0, SIZE_OF_BUFFER, NULL);

    //调整下面的数值，可以发现，当生产者个数多于消费者个数时，
    //生产速度快，生产者经常等待消费者；反之，消费者经常等待
    const unsigned short PRODUCERS_COUNT = 12;  //生产者的个数
    const unsigned short CONSUMERS_COUNT = 1;  //消费者的个数

    //总的线程数
    const unsigned short THREADS_COUNT = PRODUCERS_COUNT + CONSUMERS_COUNT;

    HANDLE hThreads[THREADS_COUNT]; //各线程的handle
    DWORD producerID[PRODUCERS_COUNT]; //生产者线程ID
    DWORD consumerID[CONSUMERS_COUNT]; //消费者线程ID
    int i;
    //创建生产者线程
    for (i = 0; i < PRODUCERS_COUNT; ++i) {
        hThreads[i] = CreateThread(NULL, 0, Producer, NULL, 0, &producerID[i]);
        if (hThreads[i] == NULL) return -1;
    }
    //创建消费者线程
    for (i = 0; i < CONSUMERS_COUNT; ++i) {
        hThreads[PRODUCERS_COUNT + i] = CreateThread(NULL, 0, Consumer, NULL, 0, &consumerID[i]);
        if (hThreads[PRODUCERS_COUNT + i] == NULL) return -1;
    }

    if (getchar()) { //按回车后终止程序运行
        g_continue = false;
    }

    return 0;
}

//把新生产的产品放入缓冲区
void AddToBuffer() {
    //以"a"（追加内容）的形式打开文件，将新的缓冲区情况写入文件
    FILE *fp;
    if ((fp = fopen("info.txt", "a")) == NULL) {
        cout << "错误,文件打不开,请检查文件名" << endl;
        return;
    }

    ProductID++;
    if (ProductID % 10 == 1) {
        fprintf(fp, "\nProduce the %dst product to buffer\n", ProductID);
        cout << "\nProduce the " << ProductID << "st product to buffer" << endl;
    } else if (ProductID % 10 == 2) {
        fprintf(fp, "\nProduce the %dnd product to buffer\n", ProductID);
        cout << "\nProduce the " << ProductID << "nd product to buffer" << endl;
    } else {
        fprintf(fp, "\nProduce the %dth product to buffer\n", ProductID);
        cout << "\nProduce the " << ProductID << "th product to buffer" << endl;
    }

    //将产品放入缓冲区，更新缓冲区下标
    g_buffer[in] = ProductID;
    in = (in + 1) % SIZE_OF_BUFFER;

    //输出缓冲区当前的状态
    for (int i = 0; i < SIZE_OF_BUFFER; i++) {
        fprintf(fp, "%d: %d", i, g_buffer[i]);
        cout << i << ": " << g_buffer[i];
        if (i == in) {
            fprintf(fp, " <-- 可放下一个产品的位置");
            cout << " <-- 可放下一个产品的位置";
        }
        if (i == out) {
            fprintf(fp, " <-- 下一个可取出产品消费的位置");
            cout << " <-- 下一个可取出产品消费的位置";
        }
        fprintf(fp, "\n"); cout << endl;
    }
    fclose(fp);
}

//从缓冲区中取出一个产品
void TakeFromBuffer() {
    //以"a"（追加内容）的形式打开文件，将新的缓冲区情况写入文件
    FILE *fp;
    if ((fp = fopen("info.txt", "a")) == NULL) {
        cout << "错误,文件打不开,请检查文件名" << endl;
        return;
    }

    //从缓冲区中取出一个产品，更新缓冲区下标
    ConsumeID = g_buffer[out];

    g_buffer[out] = 0;

    out = (out + 1) % SIZE_OF_BUFFER;

    if (ConsumeID % 10 == 1) {
        fprintf(fp, "\nConsume the %dst product from buffer... \n", ConsumeID);
        cout << "\nConsume the " << ConsumeID << "st from buffer" << endl;
    } else if (ConsumeID % 10 == 2) {
        fprintf(fp, "\nConsume the %dnd product from buffer... \n", ConsumeID);
        cout << "\nConsume the " << ConsumeID << "nd from buffer" << endl;
    } else {
        fprintf(fp, "\nConsume the %dth product from buffer... \n", ConsumeID);
        cout << "\nConsume the " << ConsumeID << "th from buffer" << endl;
    }

    //输出缓冲区当前的状态
    for (int i = 0; i < SIZE_OF_BUFFER; ++i) {
        fprintf(fp, "%d: %d", i, g_buffer[i]);
        cout << i << ": " << g_buffer[i];
        if (i == in) {
            fprintf(fp, " <-- 可放下一个产品的位置");
            cout << " <-- 可放下一个产品的位置";
        }
        if (i == out) {
            fprintf(fp, " <-- 下一个可取出产品消费的位置");
            cout << " <-- 下一个可取出产品消费的位置";
        }
        fprintf(fp, "\n"); cout << endl;
    }
    fclose(fp);
}

//生产者
DWORD WINAPI Producer(LPVOID lpPara) {
    while (g_continue) {
        WaitForSingleObject(g_hEmptySemaphore, INFINITE);
        WaitForSingleObject(g_hMutex, INFINITE);
        AddToBuffer();
        Sleep(500);
        ReleaseMutex(g_hMutex);
        ReleaseSemaphore(g_hFullSemaphore, 1, NULL);
    }
    return 0;
}

//消费者
DWORD WINAPI Consumer(LPVOID lpPara) {
    while (g_continue) {
        WaitForSingleObject(g_hFullSemaphore, INFINITE);
        WaitForSingleObject(g_hMutex, INFINITE);
        TakeFromBuffer();
        Sleep(500);
        ReleaseMutex(g_hMutex);
        ReleaseSemaphore(g_hEmptySemaphore, 1, NULL);
    }
    return 0;
}